﻿////
////
//// Big thanks to: http://harizanov.com/2013/12/control-lg-smart-tv-over-the-internet-using-a-raspberry-pi/
////
//// Supported Command codes: http://developer.lgappstv.com/TV_HELP/topic/lge.tvsdk.references.book/html/UDAP/UDAP/Annex%20A%20Table%20of%20virtual%20key%20codes%20on%20remote%20Controller.htm?path=4_0_1_5#_Toc353804463
////
////

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Net;
using System.Net.Sockets;
using System.Collections;
using System.Net.Http;
using System.Xml;
using System.Threading.Tasks;

namespace LGTVRemoteForDesktop
{
	public partial class Form1 : Form
	{
		private string sessionID = "";
		Hashtable TVs = new Hashtable();

		private bool isDiscoveryInProgress = false;
		private object lockObj = new object();

		public Form1()
		{
			InitializeComponent();
		}
		private void Form1_Load(object sender, EventArgs e)
		{
			SearchForTVs();
		}
		public void AddLog(string message)
		{
			if (this.InvokeRequired)
			{
				this.Invoke(new MethodInvoker(() => { AddLog(message); }));
				return;
			}

			textBox1.AppendText(message + Environment.NewLine);
		}

		private void DiscoverBtn_Click(object sender, EventArgs e)
		{
			SearchForTVs();
		}

		private async void SearchForTVs()
		{
			lock (lockObj)
			{
				if (isDiscoveryInProgress)
				{
					AddLog("Discovery already running! Please wait...");
					return;
				}

				isDiscoveryInProgress = true;
			}

			try
			{
				AddLog("Searching for TV's.. Please wait... ");

				TVs = await Task.Run(() => LGDiscoverer.SearchLGTV());

				TVListBox.Items.Clear();

				foreach (string key in TVs.Keys)
				{
					TVListBox.Items.Add(key);
					AddLog(TVs[key].ToString() + " : " + key);
				}

				if (TVListBox.Items.Count > 0)
				{
					TVListBox.SelectedIndex = 0;
				}
			}
			finally
			{
				isDiscoveryInProgress = false;
			}
			
		}

		private async void ShowKeyBtn_Click(object sender, EventArgs e)
		{
			try
			{
				// show key
				await Task.Run(() =>
				{
					using (var client = new HttpClient())
					{
						var content = @"<?xml version=""1.0"" encoding=""utf-8""?><envelope><api type=""pairing""><name>showKey</name></api></envelope>";
						content = "<auth><type>AuthKeyReq</type></auth>";
						client.BaseAddress = GetTVUri();
						HttpContent con = new StringContent(content, Encoding.UTF8, "text/xml");
						var result = client.PostAsync("/roap/api/auth", con).Result;
						string resultContent = result.Content.ReadAsStringAsync().Result;
						AddLog(resultContent);
					}
				});
			}
			catch (Exception ex)
			{
				MessageBox.Show(ex.Message);
			}
		}

		private async void SendKeyBtn_Click(object sender, EventArgs e)
		{
			try
			{
				await Task.Run(() =>
				{
					// get sessionID
					using (var client = new HttpClient())
					{
						var content = "<auth><type>AuthReq</type><value>" + KeyBox.Text + "</value></auth>";
						client.BaseAddress = GetTVUri();
						HttpContent con = new StringContent(content, Encoding.UTF8, "text/xml");
						var result = client.PostAsync("/roap/api/auth", con).Result;
						string resultContent = result.Content.ReadAsStringAsync().Result;

						AddLog(resultContent);

						XmlDocument doc = new XmlDocument();

						doc.LoadXml(resultContent);
						sessionID = doc.SelectSingleNode("//envelope/session").InnerText;
					}
				});
			}
			catch (Exception ex)
			{
				MessageBox.Show(ex.Message);
			}
		}

		private async void ExecCommand(int commandCode)
		{
			try
			{
				await Task.Run(() =>
				{
					// get sessionID
					using (var client = new HttpClient())
					{
						var content = "<command><name>HandleKeyInput</name><value>" + commandCode.ToString() + "</value></command>";
						client.BaseAddress = GetTVUri();
						HttpContent con = new StringContent(content, Encoding.UTF8, "text/xml");
						var result = client.PostAsync("/roap/api/command", con).Result;
						string resultContent = result.Content.ReadAsStringAsync().Result;

						AddLog(resultContent);
					}
				});
			}
			catch (Exception ex)
			{
				MessageBox.Show(ex.Message);
			}
		}

		private async Task<object> GetDataCommand(string command)
		{
			object resultContent = null;
			try
			{
				await Task.Run(() =>
				{
					// get sessionID
					using (var client = new HttpClient())
					{
						client.BaseAddress = GetTVUri();
						var result = client.GetAsync("/roap/api/data?target=" + command).Result;

						switch (command)
						{
							case "curr_channel":
								resultContent = result.Content.ReadAsStringAsync().Result;
								break;

							case "channel_list":
								resultContent = result.Content.ReadAsStringAsync().Result;
								break;

							case "context_ui":
								resultContent = result.Content.ReadAsStringAsync().Result;
								break;

							case "volume_info":
								resultContent = result.Content.ReadAsStringAsync().Result;
								break;

							case "screen_image":
								byte[] data = result.Content.ReadAsByteArrayAsync().Result;
								TypeConverter tc = TypeDescriptor.GetConverter(typeof(Bitmap));
								resultContent = (Bitmap)tc.ConvertFrom(data);
								break;

							case "is_3d":
								resultContent = result.Content.ReadAsStringAsync().Result;
								break;

							case "AppExecute":
								//resultContent = result.Content.ReadAsByteArrayAsync().Result;
								break;
						}

						if (resultContent != null)
						{
							AddLog("result data length : " + resultContent.GetType().Name.ToString());
						}
					}
				});

				
			}
			catch (Exception ex)
			{
				MessageBox.Show(ex.Message);
			}
			
			return resultContent;
		}


		private void VolUpBtn_Click(object sender, EventArgs e)
		{
			ExecCommand(24);
		}

		private void VolDownBtn_Click(object sender, EventArgs e)
		{
			ExecCommand(25);
		}

		private void ChannelDownBtn_Click(object sender, EventArgs e)
		{
			ExecCommand(28);
		}

		private void ChannelUpBtn_Click(object sender, EventArgs e)
		{
			ExecCommand(27);
		}

		private void TVListBox_SelectedIndexChanged(object sender, EventArgs e)
		{
			ipBox.Text = TVListBox.SelectedItem.ToString();
		}

		private Uri GetTVUri()
		{
			if (ipBox.Text.Trim().Length == 0 || portBox.Text.Length == 0)
			{
				string msg = "Please specify correct IP and port of your LG TV!";
				AddLog(msg);
				throw new Exception(msg);
			}

			return new Uri("http://" + ipBox.Text + ":" + portBox.Text);
		}

		private void MuteBtn_Click(object sender, EventArgs e)
		{
			ExecCommand(26);
		}

		private void PowerOffBtn_Click(object sender, EventArgs e)
		{
			ExecCommand(1);
		}

		private void SendCommandBtn_Click(object sender, EventArgs e)
		{
			try
			{
				ExecCommand(Convert.ToInt32(CommandCodeBox.Text.Trim()));
			}
			catch (Exception ex)
			{
				AddLog(ex.Message);
			}
		}

		private async void RequestDataBtn_Click(object sender, EventArgs e)
		{
			string command = CommandList.SelectedItem.ToString();
			object result = await GetDataCommand(command);

			if (result != null)
			{
				if (result.GetType() == typeof(string))
				{
					AddLog("Result: " + result.ToString());
				}
				else if (result.GetType() == typeof(Bitmap) || result.GetType() == typeof(Image))
				{
					pictureBox1.Image = (Image)result;
					AddLog("Result: bitmap");
				}
			}
			else
				AddLog("NullResult");

		}

	}
}